package kr.kwakyc.puzzle;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.util.Random;

import javax.swing.ImageIcon;
import javax.swing.JPanel;

public class ImagePanel extends JPanel{
	private Game game;
	
	private Level level;
	
	private String filePath;
	private int tileSize;
	private int tileBorder = 3;
	private Image image;
	private Image blankImage;
	private Tile[][] tiles;
	
	private int imageWidth = 400;
	private int imageHeight = 400;
	
	private int blankLocationX, blankLocationY;
	private int[][] locationWeight = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
	
	public ImagePanel(String filePath, int tileSize, Level level, Game game) {
		this.game = game;
		this.filePath = filePath;
		this.tileSize = tileSize;
		
		this.level = level;
		
		init();
		shuffle();
	}
	

	private void init() {
		image = getToolkit().getImage(filePath);
		blankImage = new ImageIcon(getToolkit().getImage("blank.jpg")).getImage();
		
		if(image != null){
			image = new ImageIcon(image).getImage();
			int width = imageWidth + (tileSize * tileBorder);
			int height = imageHeight + (tileSize * tileBorder);
			this.setPreferredSize(new Dimension(width, height));
			this.setLayout(new GridLayout(tileSize, tileSize));
			
			makeImageTile();
		}
	}

	private void makeImageTile() {
		BufferedImage bufferedImage = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g2d = bufferedImage.createGraphics();
		g2d.drawImage(image, 0, 0, imageWidth, imageHeight, 0, 0, image.getWidth(null), image.getHeight(null), null);
		g2d.dispose();
		
		int subImageWidth = imageWidth / tileSize;
		int subImageHeight = imageHeight / tileSize;
		
		tiles = new Tile[tileSize][tileSize];
		for(int i = 0; i < tileSize; i++){
			for(int j = 0; j < tileSize; j++){
				int startX = j * subImageWidth;
				int startY = i * subImageHeight;
				BufferedImage subImage = bufferedImage.getSubimage(startX, startY, subImageWidth, subImageHeight);
				
				Tile tile = new Tile(subImage);
				tile.setPreferredSize(new Dimension(subImageWidth + tileBorder, subImageHeight + tileBorder));
				tile.setImagePanel(this);
				tile.setCurrentLocation(i, j);
				tiles[i][j] = tile;
				this.add(tile);
			}
		}
		
		BufferedImage blankBufferedImage = new BufferedImage(subImageWidth, subImageHeight, BufferedImage.TYPE_INT_ARGB);
		g2d = blankBufferedImage.createGraphics();
		g2d.drawImage(blankImage, 0, 0, subImageWidth, subImageHeight, 0, 0, blankImage.getWidth(null), blankImage.getHeight(null), null);
		g2d.dispose();
		
		tiles[tileSize-1][tileSize-1].setImage(blankBufferedImage);
		tiles[tileSize-1][tileSize-1].setSolutionImage(blankBufferedImage);
		blankLocationX = tileSize - 1;
		blankLocationY = tileSize - 1;
	}
	
	public void shuffle(){
		int counter = 1000;
		
		switch(level){
		case EASY:
			counter = 100;
			break;
		case NORMAL:
			counter = 1000;
			break;
		case HARD:
			counter = 3000;
			break;
		}
		
		Random random = new Random();
		for(int i = 0; i < counter; i++){
			int randomValue = random.nextInt(4);
			if(randomValue == 0)		move(blankLocationX, blankLocationY - 1);
			else if(randomValue == 1)	move(blankLocationX, blankLocationY + 1);
			else if(randomValue == 2)	move(blankLocationX - 1, blankLocationY);
			else if(randomValue == 3)	move(blankLocationX + 1, blankLocationY);
		}
	}
	
	public void tileClicked(int i, int j){
		move(i, j);
		if(isClear())		game.gameClear();
	}


	public void move(int i, int j) {
		if(canMove(i, j)){
			swap(i, j);
		}
	}

	private boolean canMove(int i, int j) {
		
		if(i >= 0 && i < tileSize && j >= 0 && j < tileSize){
			for(int locationWeightI = 0; locationWeightI < locationWeight.length; locationWeightI++){
				int expectedI = blankLocationX + locationWeight[locationWeightI][0];
				int expectedJ = blankLocationY + locationWeight[locationWeightI][1];
				
				if(i == expectedI && j == expectedJ)		return true;
			}
		}
		
		return false;
	}

	private void swap(int i, int j) {
		Tile blankTile = tiles[blankLocationX][blankLocationY];
		Tile targetTile = tiles[i][j];
		
		BufferedImage tmpImage = blankTile.getImage();
		blankTile.setImage(targetTile.getImage());
		targetTile.setImage(tmpImage);
		
		blankTile.repaint();
		targetTile.repaint();
		
		blankLocationX = i;
		blankLocationY = j;
	}
	
	private boolean isClear() {
		if(blankLocationX == tileSize - 1 && blankLocationY == tileSize - 1){
			for(int i = 0; i < tileSize; i++){
				for(int j = 0; j < tileSize; j++){
					if(!tiles[i][j].isRight()){
						return false;
					}
				}
			}
			
			return true;
		}
		
		return false;
	}
	
	public Image getImage(){
		return image;
	}
}
